/*	--- 使用DAO --- 
(说白了就是一个JdbcTemplate的Proxy)


在[传统的多层应用程序]中，通常是[Web层]调用[业务层]，[业务层]调用[数据访问层]。

[业务层]负责处理[各种业务逻辑]，而[数据访问层]只负责[对数据进行"增删改查"]。

因此，[实现数据访问层]就是用[JdbcTemplate]实现[对数据库的操作]。

· Web层 -> Transaction层 -> 数据访问层(DAO, Data Access Object)



编写[数据访问层]的时候，可以使用【DAO模式】。
(DAO, Data Access Object的缩写)


它没有什么神秘之处，实现起来基本如下： */
public class UserDao {

	@Autowired 
	JdbcTemplate jdbcTemplate;

	User getById(long id) {
		//...
	}

	List<User> getUsers(int page) {
		//...
	}

	User createUser(User uer) {
		//...
	}

	User updateUser(User user) {
		//...
	}

	void deleteUser(User user) {
		//...
	}
}


/*
Spring提供了一个[JdbcDaoSupport类]，用于简化DAO的实现。


这个[JdbcDaoSupport]没什么复杂的

[核心代码]就是[持有一个JdbcTemplate]： */
public abstract class JdbcDaoSupport extends DaoSupport {

	privte JdbcTemplate jdbcTemplate;
		// 区别于DAO, 没有标记 @Autowired


	public final void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
		initTemplateConfig();
	}

	public final JdbcTemplate getJdbcTemplate() {
		return this.jdbcTemplate;
	}

	//...
}


/*
JdbcDaoSupport的意图是: [子类]直接从[JdbcDaoSupport]继承后，可以随时调用 getJdbcTemplate() 获得[JdbcTemplate的实例]。

那么问题来了：因为【JdbcDaoSupport的jdbcTemplate字段】没有标记【@Autowired】，所以，【子类】想要【注入JdbcTemplate】，还得【自己想个办法】： */
@Component 
@Transactional 
public class UserDao extends JdbcDaoSupport {
	@Autowired 
	JdbcTemplate jdbcTemplate;

	@PostConstruct 
	public void init() {
		super.setJdbcTemplate(jdbcTemplate);
	}
}

/*
有的童鞋可能看出来了：既然 UserDao 都已经注入了【JdbcTemplate】，那再把【它】放到【父类】里，通过【getJdbcTemplate()访问】岂不是多此一举？

如果使用【传统的XML配置】，并【不需要编写@Autowired JdbcTemplate jdbcTemplate】

但是考虑到【现在基本上是使用注解的方式】，我们可以【编写一个AbstractDao】，专门负责【注入JdbcTemplate】： */
public abstract class AbstractDao extends JdbcDaoSupport {
	@Autowired 
	private JdbcTemplate jdbcTemplate;

	@PostConstruct 
	public void init() {
		super.setJdbcTemplate(jdbcTemplate);
	}
}

/*
这样，【子类的代码】就非常干净，可以直接调用 getJdbcTemplate() ： */
@Component 
@Transactional 
public class UserDao extends AbstractDao {
	public User getById(long id) {
		return getJdbcTemplate().queryForObject(
			"Select * From users Where id = ?",
			new BeanPropertyRowMapper<>(User.class),
			id
		);
	}

	//...
}


/*
倘若肯【再多写一点"样板代码"】，

就可以把[abstract class AbstractDao]改成[泛型 AbstractDao<T> ]，并实现 
	· getById()
	· getAll()
	· deleteById()

这样的[通用方法]： */
public abstract class AbstractDao<T> extends JdbcDaoSupport {

	private String table;
	private Class<T> entityClass;
	private RowMapper<T> rowMapper;

	public AbstractDao() {
		// 获取[当前类型]的[泛型类型]
		this.entityClass = getParameterizedType();
		this.table = this.entityClass.getSimpleName().toLowerCase() + "s";
		this.rowMapper = new BeanPropertyRowMapper<>(entityClass);
	}

	public T getById(long id) {
		return getJdbcTemplate().queryForObject(
			"Select * From " + table + " Where id = ? ", 
			this.rowMapper, 
			id
		);
	}

	public List<T> getAll(int pageIndex) {
		int limit = 100;
		int offset = limit * (pageIndex - 1);

		return getJdbcTemplate().query(
			"Select * From " + table + " LIMIT ? OFFSET ? ",
			new Object[] { limit, offset },
			this.rowMapper
		);
	}

	public void deleteById(long id) {
		getJdbcTemplate().update(
			"Delete From " + table + " Where id = ? ", 
			id
		);
	}

	//...
}


/*
这样，【每个子类】就自动获得了【这些通用方法】： 
	· getById()
	· getAll()
	· deleteById()
*/
@Component 
@Transactional 
public class UserDao extends AbstractDao<User> {
	// 已经有了:
	    // User getById(long)
	    // List<User> getAll(int)
	    // void deleteById(long)
}

@Component
@Transactional
public class BookDao extends AbstractDao<Book> {
    // 已经有了:
    // Book getById(long)
    // List<Book> getAll(int)
    // void deleteById(long)
}

/*
可见，[DAO模式]就是[一个简单的数据访问模式]，是否[使用DAO]，根据[实际情况决定]

因为很多时候，直接在[Service层]操作[数据库]也是[完全没有问题的]。


===========================================================

#	练习 

使用DAO模式  */


/*=========================================================

#	----- 使用DAO の 小结 -----

1. Spring提供了JdbcDaoSupport来便于我们实现DAO模式；

2. 可以基于[泛型]实现[更通用、更简洁的DAO模式]。


*/










